package io.github.uicdb.liba.api.register;

import net.minecraft.resources.ResourceLocation;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.world.item.BucketItem;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.block.LiquidBlock;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.material.Material;
import net.minecraftforge.client.model.generators.ConfiguredModel;
import net.minecraftforge.client.model.generators.ItemModelBuilder;
import net.minecraftforge.client.model.generators.ModelFile;
import net.minecraftforge.client.model.generators.loaders.DynamicBucketModelBuilder;
import net.minecraftforge.fluids.FluidAttributes;
import net.minecraftforge.fluids.ForgeFlowingFluid;
import org.jetbrains.annotations.Nullable;

import java.util.HashMap;
import java.util.Objects;
import java.util.function.Consumer;

@SuppressWarnings("unused")
public  class FluidBuilder {
    private static final ResourceLocation DRIPPING_BUCKET = new ResourceLocation("forge", "bucket_drip");
    private final LazyRegistry lazyRegistry;
    private final String name;
    private final String flowName;
    private final HashMap<String, String> langMap = new HashMap<>();
    private final HashMap<String, String> flowingLangMap = new HashMap<>();
    private final FluidHolder fluidHolder = new FluidHolder();
    private ResourceLocation stillLoc;
    private ResourceLocation flowLoc;
    private Consumer<FluidAttributes.Builder> builderConsumer;
    private Consumer<ForgeFlowingFluid.Properties> propertiesConsumer;
    private BlockHolder<? extends LiquidBlock> block;
    private ItemHolder<? extends BucketItem> bucket;
    private Consumer<DynamicBucketModelBuilder<ItemModelBuilder>> bucketModelBuilderConsumer;
    private int lightLevel = 1;
    private boolean isGas = false;
    private boolean noBucket = false;
    private ResourceLocation bucketTexture;

    protected FluidBuilder(LazyRegistry lazyRegistry, String name) {
        this.lazyRegistry = lazyRegistry;
        this.name = name;
        this.flowName = name + "_flow";
        stillLoc = new ResourceLocation(lazyRegistry.modid, "block/" + name);
        flowLoc = new ResourceLocation(lazyRegistry.modid, "block/" + flowName);
    }

    public FluidBuilder gas() {
        isGas = true;
        return this;
    }

    public FluidBuilder stillTexture(ResourceLocation texture) {
        this.stillLoc = texture;
        return this;
    }

    public FluidBuilder attribute(Consumer<FluidAttributes.Builder> builderConsumer) {
        this.builderConsumer = builderConsumer;
        return this;
    }

    public FluidBuilder useWaterTexture() {
        stillLoc = new ResourceLocation("block/water_still");
        flowLoc = new ResourceLocation("block/water_flow");
        return this;
    }

    public FluidBuilder useTexture(ResourceLocation stillLoc) {
        this.stillLoc = stillLoc;
        this.flowLoc = new ResourceLocation(stillLoc.getNamespace(), stillLoc.getPath() + "_flow");
        return this;
    }

    public FluidBuilder flowTexture(ResourceLocation texture) {
        this.flowLoc = texture;
        return this;
    }

    public FluidBuilder block(BlockHolder<? extends LiquidBlock> liquidBlockHolder) {
        this.block = liquidBlockHolder;
        return this;
    }

    public FluidBuilder light(int lightLevel) {
        this.lightLevel = lightLevel;
        return this;
    }

    public FluidBuilder noBucket() {
        this.noBucket = true;
        return this;
    }

    public FluidBuilder bucket(Consumer<DynamicBucketModelBuilder<ItemModelBuilder>> bucketModelBuilderConsumer) {
        this.bucketModelBuilderConsumer = bucketModelBuilderConsumer;
        return this;
    }

    public FluidBuilder bucketTexture(ResourceLocation bucketTexture) {
        this.bucketTexture = bucketTexture;
        return this;
    }

    public FluidBuilder name(String lang, String value) {
        langMap.put(lang, value);
        return this;
    }

    public FluidBuilder flowName(String lang, String value) {
        flowingLangMap.put(lang, value);
        return this;
    }

    public FluidBuilder properties(Consumer<ForgeFlowingFluid.Properties> propertiesConsumer) {
        this.propertiesConsumer = propertiesConsumer;
        return this;
    }

    private void defaultName() {
        name("en_us", LangBuilder.toTitleCase(name));
    }

    private void defaultFlowName() {
        flowName("en_us", "Flowing " + LangBuilder.toTitleCase(name));
    }

    public FluidHolder build() {
        var source = lazyRegistry.fluids.register(name, () -> new ForgeFlowingFluid.Source(fluidHolder.properties));
        var flowing = lazyRegistry.fluids.register(flowName, () -> new ForgeFlowingFluid.Flowing(fluidHolder.properties));
        fluidHolder.source = source;
        fluidHolder.flow = flowing;
        if (!noBucket)
            bucket = registerBucket();
        if (block == null && !isGas) {
            block = registerBlock(bucket);
            fluidHolder.sourceBlock = block;
        }
        FluidAttributes.Builder builder = FluidAttributes.builder(stillLoc, flowLoc);
        Objects.requireNonNullElse(builderConsumer, b -> b.temperature(50).viscosity(10).luminosity(1).sound(SoundEvents.WATER_AMBIENT)).accept(builder);
        if (isGas) builder.gaseous();
        fluidHolder.properties = new ForgeFlowingFluid.Properties(fluidHolder.source(), fluidHolder.flow(), builder).block(block).bucket(bucket);
        Objects.requireNonNullElse(propertiesConsumer, p -> p.block(block).slopeFindDistance(3).explosionResistance(100f)).accept(fluidHolder.properties);
        if (!langMap.containsKey(lazyRegistry.defaultLang)) defaultName();
        if (!flowingLangMap.containsKey(lazyRegistry.defaultLang)) defaultFlowName();
        langMap.forEach((lang, value) -> lazyRegistry.lang(lang, () -> new TranslationBean("fluid." + lazyRegistry.modid + "." + name, value)));
        flowingLangMap.forEach((lang, value) -> lazyRegistry.lang(lang, () -> new TranslationBean("fluid." + lazyRegistry.modid + "." + flowName, value)));
        return fluidHolder;
    }

    private ItemHolder<BucketItem> registerBucket() {
        return lazyRegistry.item(name + "_bucket", p -> new BucketItem(fluidHolder.source(), p.stacksTo(1).craftRemainder(Items.BUCKET)))
                .defaultName("Bucket Of " + LangBuilder.toTitleCase(name))
                .model((itemModelProvider, itemHolder) -> {
                    var item = itemHolder.get();
                    var id = item.getRegistryName();
                    itemModelProvider.withExistingParent(id.getPath(), DRIPPING_BUCKET)
                            .texture("base", Objects.requireNonNullElse(bucketTexture, itemModelProvider.mcLoc("item/bucket")))
                            .customLoader((itemModelBuilder, existingFileHelper) -> {
                                DynamicBucketModelBuilder<ItemModelBuilder> dynamicBucketModelBuilder = DynamicBucketModelBuilder.begin(itemModelBuilder, existingFileHelper);
                                Objects.requireNonNullElse(bucketModelBuilderConsumer, modelBuilder -> {
                                }).accept(dynamicBucketModelBuilder);
                                return dynamicBucketModelBuilder;
                            })
                            .flipGas(isGas)
                            .fluid(fluidHolder.get());
                })
                .build();
    }

    private BlockHolder<LiquidBlock> registerBlock(@Nullable ItemHolder<? extends BucketItem> bucketItem) {
        var liquidBlockBlockHolder = lazyRegistry.blocks.register(name, () -> new LiquidBlock(fluidHolder.source(), BlockBehaviour.Properties.of(Material.WATER).lightLevel(state -> lightLevel)));
        var result = new BlockHolder<>(bucketItem, liquidBlockBlockHolder);
        ;
        lazyRegistry.iBlockStateHashMap.put(result, (provider, blockHolder) -> provider.getVariantBuilder(blockHolder.get()).forAllStates(state -> new ConfiguredModel[]{new ConfiguredModel(new ModelFile.UncheckedModelFile(new ResourceLocation("minecraft:block/water")))}));
        return result;
    }
}